package devices;

public class Timer implements java.lang.Runnable, core.interfaces.DeviceInterface{

	private int dataAddr = 0xFFFD;
	private int controlAddr = 0xFFFC;
	private int interruptLine = 1;

	private java.lang.Object lock = new java.lang.Object();
	private java.lang.Thread thread = null;

	private boolean oneShot = true;
	private int initCount = 0;
	private boolean generateInterrupt = false;
	private boolean running = false;

	private core.interfaces.InterruptInterface iInterface = null;
	private core.MemoryClass mem;

	private short copyTimerControl = 0;
	private short copyTimerData = 0;

	private int count = 0;
	static private long period = 100; 

	public Timer(){
		init(null, null, this.controlAddr, this.interruptLine);
	}
	public Timer(core.MemoryClass mem, core.interfaces.InterruptInterface iObject){
		init(mem, iObject, this.controlAddr, this.interruptLine);
	}
	public Timer(core.MemoryClass mem, core.interfaces.InterruptInterface iObject, int baseAddr, int interruptLine){
		init(mem, iObject, baseAddr, interruptLine);
	}
	private void init(core.MemoryClass mem, core.interfaces.InterruptInterface iObject, int baseAddr, int interruptLine){
		this.mem = mem;
		this.iInterface = iObject;
		this.thread = new java.lang.Thread(this);
		this.thread.start();
		this.setBaseAddress(baseAddr);
		this.setInterruptLine(interruptLine);
	}
	public void reset(){
		this.copyTimerControl = 0;
		this.copyTimerData = 0;
		this.stop();
		this.configureTimer(this.copyTimerControl, this.copyTimerData);
	}
	public void reloadConfiguration(){
		if(this.mem != null){
			short auxControl, auxData;
			auxControl = this.mem.getWord(this.controlAddr);
			auxData = this.mem.getWord(this.dataAddr);
			if(auxControl != this.copyTimerControl || auxData != this.copyTimerData){
				util.Messages.info(String.format("TIMER: Reload config with 0x%08x(0x%08x) 0x%08x(0x%08x)\n", auxControl, this.copyTimerControl, auxData, this.copyTimerData));
				this.copyTimerControl = auxControl;
				this.copyTimerData = auxData;
				configureTimer(this.copyTimerControl, this.copyTimerData);
				//Reset start bit
				this.copyTimerControl = (short)(0x7FFE & this.copyTimerControl);
				this.mem.setWord(this.controlAddr, this.copyTimerControl);
			}else{
				util.Messages.info(String.format("TIMER: NOT Reloading config with 0x%04x(0x%04x) 0x%04x(0x%04x)\n", auxControl, this.copyTimerControl, auxData, this.copyTimerData));
			}
		}
	}

	public void configureTimer(short control, short data){
		if((control & 0x8000)!=0){
			this.initCount = (0x0000FFFF & data);
			if((control & 0x0002)!=0){
				this.oneShot = true;
			}else{
				this.oneShot = false;
			}
			if((control & 0x0004)!=0){
				this.generateInterrupt = true;
			}else{
				this.generateInterrupt = false;
			}
			if((control & 0x0001)!=0){
				this.start();
			}else{
				this.stop();
			}
		}
	}

	public void run(){
		long sleepTime = devices.Timer.period;
		long lastMilis = java.util.Calendar.getInstance().getTimeInMillis();
		long newMilis = 0;
		while(true){
			synchronized(lock){
				while(!this.running){
					try {
						util.Messages.info(String.format("TIMER: Go to sleep\n"));
						this.lock.wait();
					} catch (InterruptedException e) {}
					util.Messages.info(String.format("TIMER: Wakeup!\n"));
					lastMilis = java.util.Calendar.getInstance().getTimeInMillis() - 100;
				}
			}
			try {
				newMilis = java.util.Calendar.getInstance().getTimeInMillis();
				sleepTime = (devices.Timer.period * 2) - (newMilis - lastMilis);
				lastMilis = newMilis;
				if(sleepTime>0){
					java.lang.Thread.sleep(sleepTime);
				}
			} catch (InterruptedException e) {}
			count--;
			if(count == 0){
				util.Messages.info(String.format("TIMER: Final Countdown\n"));
				if(this.generateInterrupt){
					if(this.iInterface != null){
						this.iInterface.raiseInterruptLine(this.interruptLine);
					}
				}
				if(!this.oneShot){
					this.count = this.initCount;
				}else{
					this.running = false;
				}
			}
		}
	}
	private void start(){
		this.running = true;
		this.count = this.initCount;
		synchronized(lock){
			this.lock.notifyAll();
		}
	}
	private void stop(){
		this.running = false;
	}

	/**
	 * DeviceInterface Methods:
	 */
	public int getBaseAddress() {
		return(this.controlAddr);
	}
	public int getInterruptLine() {
		return(this.interruptLine);
	}
	public int getMemSize() {
		return(2);
	}
	public void setBaseAddress(int baseAddr) {
		this.controlAddr = baseAddr;
		this.dataAddr = baseAddr + 1;
		this.reloadConfiguration();
	}
	public void setInterruptLine(int line) {
		this.interruptLine = line;
	}
}
